//#include "herm/base/container.h"
#include "herm/base/utils/types.h"
#include "herm/base/utils/misc.h"
#include "herm/base/socket/socket_def.h"
#include "herm/base/socket/stream.h"
#include "sock_buffer.h"

StreamBuffer::StreamBuffer(unsigned int size)
	: herm::Buffer(size)
{
}

StreamBuffer::~StreamBuffer()
{
}

#ifdef ENABLE_EDGE_TRIGGER

int StreamBuffer::Receive(herm::Stream& stream)
{
	const int MAX_LOOP_COUNT = 5;
	int count = 0;
	for (; count < MAX_LOOP_COUNT; count++)
	{
		unsigned int len = m_size - m_write + m_read; // len is free space
		if (len < MIN_SPACE_SIZE)
			return herm::ST_BUFF_FULL;
			
		// Ensure that we sample the m_read index -before- we
		// start putting bytes into the kfifo.
		smp_mb();

		unsigned int len2 = min(len, m_size - (m_write & (m_size -1)));
		HERM_ASSERT(len2 != 0);

		// first put the data starting from m_write to buffer end 
		// Must process nonblocking and exception!!!!!
		int bytes = stream.ReceiveNB(m_buff + (m_write & (m_size - 1)), len2);
		if (bytes == 0)
			return herm::ST_CLOSE;

		if (bytes == herm::COMPLETION) // The socket buffer is empty
			break;

		smp_wmb();
		m_write += bytes;

		if (static_cast<unsigned int>(bytes) < len2) // Read bytes < read buffer size, the socket buffer is empty
			break;
	}

	if (count == MAX_LOOP_COUNT)
		return herm::ST_BUFF_FULL; // Return herm::ST_BUFF_FULL to Ensure enable read mask!

	return herm::ST_SUCCESS;
}

bool StreamBuffer::Send(herm::Stream& stream)
{
	while (true)
	{
		unsigned int len = m_write - m_read;
		if (len == 0) // empty
			return false;

		// Ensure that we sample the m_write index -before- we
		// start removing bytes from the kfifo.
		smp_rmb();

		// Get the data from m_read until the end of the buffer 
//		unsigned int len2 = m_size - (m_read & (m_size - 1));
		unsigned int len2 = min(len, m_size - (m_read & (m_size - 1)));
		HERM_ASSERT(len2 != 0);

		// Must process non block and exception!!!!
		int bytes = stream.SendNB(m_buff + (m_read & (m_size - 1)), len2);
		if (bytes == herm::COMPLETION)  // The socket buffer is full
			break;

		// Ensure that we remove the bytes from the kfifo -before-
		// we update the m_read index.
		smp_mb();
		m_read += bytes;
		
		if (static_cast<unsigned int>(bytes) < len2) // The socket buffer is full
			break;
	}

	return true;
}

#else

int StreamBuffer::Receive(herm::Stream& stream)
{
	unsigned int len = m_size - m_write + m_read; // len is free space
	if (len < MIN_SPACE_SIZE)
		return herm::ST_SUCCESS;

	// Ensure that we sample the m_read index -before- we
	// start putting bytes into the kfifo.
	smp_mb();

	unsigned int len2 = std::min(len, m_size - (m_write & (m_size -1)));
	HERM_ASSERT(len2 != 0);

	// first put the data starting from m_write to buffer end 
	// Must process nonblocking and exception!!!!!
	int bytes = stream.ReceiveNB(m_buff + (m_write & (m_size - 1)), len2);
	if (bytes == 0)
		return herm::ST_CLOSE;

	if (bytes == herm::COMPLETION) // The socket buffer is empty
		return herm::ST_SUCCESS;

	smp_wmb();
	m_write += bytes;

	return herm::ST_SUCCESS;
}

bool StreamBuffer::Send(herm::Stream& stream)
{
	while (true)
	{
		unsigned int len = m_write - m_read;
		if (len == 0) // empty
			return false;

		// Ensure that we sample the m_write index -before- we
		// start removing bytes from the kfifo.
		smp_rmb();

		// Get the data from m_read until the end of the buffer 
//		unsigned int len2 = m_size - (m_read & (m_size - 1));
		unsigned int len2 = std::min(len, m_size - (m_read & (m_size - 1)));
		HERM_ASSERT(len2 != 0);

		// Must process non block and exception!!!!
		int bytes = stream.SendNB(m_buff + (m_read & (m_size - 1)), len2);
		if (bytes == herm::COMPLETION)  // The socket buffer is full
			break;

		// Ensure that we remove the bytes from the kfifo -before-
		// we update the m_read index.
		smp_mb();
		m_read += bytes;
		
		if (static_cast<unsigned int>(bytes) < len2) // The socket buffer is full
			break;
	}

	return true;
}
#endif

/*
DatagramBuffer::DatagramBuffer(unsigned int size)
	: herm::Buffer(size)
{
}

DatagramBuffer::~DatagramBuffer()
{
}

int DatagramBuffer::Receive(herm::Datagram& datagram)
{
	const int MAX_LOOP_COUNT = 5;
	int count = 0;
	for (; count < MAX_LOOP_COUNT; count++)
	{
		unsigned int len = m_size - m_write + m_read; // len is free space
		if (len < herm::MAX_UDP_MSG_LEN)
			return herm::ST_BUFF_FULL;
			
		// Ensure that we sample the m_read index -before- we
		// start putting bytes into the kfifo.
		smp_mb();

		unsigned int len2 = min(len, m_size - (m_write & (m_size -1)));
		HERM_ASSERT(len2 != 0);
		if (len2 < herm::MAX_UDP_MSG_LEN)
		{
			// Set msg len and indicate the data is invalid
			// ...
			m_write += len2;
			continue;
		}

		// first put the data starting from m_write to buffer end 
		// Must process nonblocking and exception!!!!!
		int bytes = datagram.ReceiveNB(m_buff + (m_write & (m_size - 1)), len2);
		if (bytes == 0 || bytes == herm::COMPLETION) // The socket buffer is empty
			break;

		smp_wmb();
		m_write += bytes;

		if (static_cast<unsigned int>(bytes) < len2) // Read bytes < read buffer size, the socket buffer is empty
			break;
	}

	if (count == MAX_LOOP_COUNT)
		return herm::ST_BUFF_FULL; // Return herm::ST_BUFF_FULL to Ensure enable read mask!

	return herm::ST_SUCCESS;
}

bool DatagramBuffer::Send(herm::Datagram& datagram, const herm::Address& addr)
{
	while (true)
	{
		unsigned int len = m_write - m_read;
		if (len == 0) // empty
			return false;

		// Ensure that we sample the m_write index -before- we
		// start removing bytes from the kfifo.
		smp_rmb();

		// Get the data from m_read until the end of the buffer 
//		unsigned int len2 = m_size - (m_read & (m_size - 1));
		unsigned int len2 = min(len, m_size - (m_read & (m_size - 1)));
		HERM_ASSERT(len2 != 0);

		// Must process non block and exception!!!!
		int bytes = datagram.SendNB(m_buff + (m_read & (m_size - 1)), len2, addr);
		if (bytes == herm::COMPLETION)  // The socket buffer is full
			break;

		// Ensure that we remove the bytes from the kfifo -before-
		// we update the m_read index.
		smp_mb();
		m_read += bytes;
		
		if (static_cast<unsigned int>(bytes) < len2) // The socket buffer is full
			break;
	}

	return true;
}*/
